/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//tutorial.hpp
// 
//quickstart tutorial to OpenMVL

#ifndef MVL_EXAMPLES_TUTORIAL_HPP
#define MVL_EXAMPLES_TUTORIAL_HPP

//mvl is a header only library, just include:
#include "../mvl.hpp"
using namespace mvl;

namespace mvl {

namespace examples {

//now we well declare some matrix and vector layouts needed later.
//A layout is a type that define the matrix structure at compile time.
//It defines the size of the matrix (rows, colums), the order (row-major or column-major)
// and if the matrix has entries that assume the same value all the time (such as
// zeros and ones for identity matrices)

typedef layout::dense_layout<4, 4, layout::row_major_tag> dense_matrix_layout_t;
//here we just declared a layout for a 4x4 matrix, where the entries are all values and
//they will be stored in memory in a row-major order. nothing special here

//let's now declare some sparse matrices:

//this defines make the declaration a little more compact:
#define e layout::e
#define _0_ layout::_0_
#define _1_ layout::_1_
#define _v_ layout::_v_

//now we'll declare an identity matrix of size 4x4, in row-major order. meta::vector is 
//a compile time vector that holds types. To define a sparse layout, we need a meta::vector of 
//meta::vectors that tells us where the non-zero entries are. An entry is defines as e<pos, tag>
//were pos represents the column index (or row index in the case of column-major layouts) and
// tag is either one of the following:
// _0_: the entry is always 0
// _1_: the entry is always 1
// _v_: the entry can assume an arbitrary value.
//Note: the _0_ entries are not really necessary, and can be omitted. For an identity matrix, 
//in row-major order, the layout can thus be declared as a meta::vector of four meta::vectors each 
//one representing a row. Each row meta::vector contains the entry representing the 1 in the identity matrix.
typedef layout::make_sparse <
    4, 4, 
    layout::row_major_tag,
    meta::vector<
        meta::vector< 
            e<0, _1_>
        >,
        meta::vector< 
            e<1, _1_>
        >,
        meta::vector< 
            e<2, _1_> 
        >,
        meta::vector< 
            e<3, _1_> 
        >  
    >
>::type identity_layout_t;

//now we'll declare a 4x4 matrix often used in 3d graphics. It is able of represent every combination 
//of translation, scale and rotation. This type of matrix can assume any value except the last row, wich is
// always [0, 0, 0, 1]
typedef layout::make_sparse<
    4, 4, 
    layout::row_major_tag,
    meta::vector<
        meta::vector<e<0, _v_>, e<1, _v_>, e<2, _v_>, e<3, _v_> >,
        meta::vector<e<0, _v_>, e<1, _v_>, e<2, _v_>, e<3, _v_> >,
        meta::vector<e<0, _v_>, e<1, _v_>, e<2, _v_>, e<3, _v_> >,
        meta::vector<e<3, _1_> >  
    >
>::type graphics_layout_t;
//note how we didn't include the _0_ entries in the last column because they are not necessary.

//a more compact way to declare the same layout as above is: 
typedef layout::make_sparse<
    4, 4, 
    layout::row_major_tag,
    meta::vector<
        meta::ivector<0, 1, 2, 3>,
        meta::ivector<0, 1, 2, 3>,
        meta::ivector<0, 1, 2, 3>,
        meta::vector<e<3, _1_> >  
    >
>::type graphics_layout2_t;
//meta::ivector allows to define a row of "_v_" entries by just specifing their index position

//one last thing, let's declare a layout for a point in 3d space. We will declare it as a 4x1 column
//vector, having the last component always equal to one. We declared it as size 4 so we can use it in 
// operations with the previously declared matrices.
typedef layout::make_sparse <
    4, 1, 
    layout::column_major_tag,
    meta::vector<
        meta::vector< e<0, _v_>, e<1, _v_>, e<2, _v_>, e<3, _1_> > 
    >
>::type point_layout_t;
//note in this case we are declaring it as column major order, so we can declare the first column as
// meta::vector< e<0, _v_>, e<1, _v_>, e<2, _v_>, e<3, _1_> > 

#undef e
#undef _0_
#undef _1_
#undef _v_

//we are done with the layouts. For some more examples of layouts, please refer to tests/layouts.hpp

//We'll now declare the actual matrix and point based on those layouts. 
//The easiest way is to derive a class from mvl::mat. Since we are deriving, in order to retain all
//the functionality of mvl::mat, we need to redeclare the appropriate constructors and inherit the operator = 

struct point_t : public mat<float, point_layout_t> 
{
    typedef mat<float, point_layout_t>  parent_t;
    typedef float                       value_type;

    //constructors (needed)
    //using parent_type::point_t; in c++0x
    point_t() : parent_t() {  }

    point_t(const point_t &p) : parent_t(p) { }

    template<typename E>
    point_t(const expr<E>& e) : parent_t(e) {  }

    point_t(float v0, float v1, float v2, float v3 = 1.0) : parent_t(v0, v1, v2) {  }

    //inherit operator =
    using parent_t::operator =;

    //some useful access
    float x() const { return parent_t::operator()(0); }
    float& x() { return parent_t::operator()(0); }

    float y() const { return parent_t::operator()(1); }
    float& y() { return parent_t::operator()(1); }

    float z() const { return parent_t::operator()(2); }
    float& z() { return parent_t::operator()(2); }

    float w() const { return float(1.0); }
};

//
struct matrix_t : public mat<float, graphics_layout_t> 
{
    typedef mat<float, graphics_layout_t>   parent_t;
    typedef float                           value_type;

    //constructors
    //using parent_type::matrix_t; in c++0x
    matrix_t() : parent_t() {  }
    matrix_t(const matrix_t &p) : parent_t(p) { }
    template<typename E>
    matrix_t(const expr<E>& e) : parent_t(e) {  }
    matrix_t(float v0, float v1, float v2, float v3,
            float v4, float v5, float v6, float v7,
            float v8, float v9, float v10, float v11) : 
        parent_t(v0, v1, v2, v3,
                 v4, v5, v6, v7,
                 v8, v9, v10, v11) {  }

    //inherit operator =
    using parent_t::operator =;

    //some useful access
    float operator()(int i, int j) const { return parent_t::operator()(i, j); }
    float& operator()(int i, int j) { return parent_t::operator()(i, j); }
};

//Note that internally, only the _v_ values are stored in memory, so the size for the point is
// sizeof(float) * 3 while for the matrix is sizeof(float) * 12
// 

//now some examples of how to use them:

void tutorial(int argc, char* argv[])
{
    std::cout << "tutorial" << std::endl;

    //init some matrices
    //specify the values (only for the "_v_" entries)
    matrix_t m0(1, 1, 2, 2,
                1, 4, 4, 5,
                3, 4, 4, 3);

    //comma initializer
    matrix_t m1;
    assign(m1) = 1, 2, 3, 4, 
                 3, 3, 4, 3,
                 4, 5, 4, 4;

    //copy constructor
    matrix_t m2(m1);
    //
    matrix_t m3;

    //declare some points
    point_t p0(1, 2, 4);
    point_t p1 = p0;
    point_t p2, p3;
    assign(p2) = 0, 2, 3;
    
    //some operations
    m3 = -m2 + 3.4f * m1; 
    m3 = 0.5f * prod(m2, m1);

    p3 = p2 - p1;

    //element by element operations
    p3 = element_mul(p1, p2);
    p3 = element_div(p1, p2);

    //matrix-point prod
    p3 = prod(m1, p2);

    //assign an identity matrix
    m3 = identity_mat<float, 4, 4>();

    //assign a scalar mat
    m3 = scalar_mat<float, 4, 4>(3.5f);

    //transpose
    m3 = 4.0f * trans(m1);

    //reference to C array:
    float m[12];
    cmat<graphics_layout_t>(m) = m2;

    m3 = m2 + 4.0 * cmat<graphics_layout_t>(m);

    //access value at 1, 1
    float _11 = m2(1, 1);
    m2(1, 1) = 3.2f;
}

} // namespace examples

} // namespace mvl

#endif





